//: src/com/silvtar/net/Server.java
/* 
 * Created by 朴晟宁 <piaosn@gmail.com> on 4/21/2013. 
 * Copyright (c) 2013 朴晟宁. All rights reserved.
 * 
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program;
 * 
 * *********************************************************************
 * 
 * Modify History:
 * Date        Version    Description
 * 4/24/2013   0.1        Create The Class
 * 4/25/2013   0.2        Move main activity into the internal class. It 
 *                        can make the class won't blocking.
 * 4/26/2013   0.2.1      Rename the interface<ServerRun> to <Acceptor>, 
 *                        method<startProcess> to <listen>.
 * 5/28/2013   0.3        Server can be closed now.                       
 */

package com.silvtar.net;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

import com.silvtar.io.StdOut;

/**
 * 服务器类，负责等待计算机连接。 使用方法：1.构造 2.调用listen() 3.每当一个客户端连接，本类会回调Acceptor.accept()。
 * 
 * @author 朴晟宁/Silvtar
 * @author piaosn@gmail.com
 * @version 0.3
 */
public class Server extends Thread {

    private Acceptor          callingClass = null;

    private InnerServer       innerServer  = null;

    private int               port         = 0;

    private ServerSocket      serverSocket = null;

    private ArrayList<Socket> socketList   = null;

    private ArrayList<Thread> threadList   = null;

    private Socket            socket       = null;

    private Thread            thread       = null;

    /**
     * Server类回调接口，用于给Server类回调
     * 
     * @author 朴晟宁/Silvtar
     * @author piaosn@gmail.com
     * @version 0.1 :: 2013/4/24
     */
    public interface Acceptor {

        public void accept(int identifier);
    }

    /**
     * 服务器内部类，主要用于隐藏细节，以及防止Server类堵塞。
     * 
     * @author 朴晟宁/Silvtar
     * @author piaosn@gmail.com
     * @version 0.1 :: 2013/4/25
     */
    private class InnerServer extends Thread {

        /**
         * 开始监听,调用这个方法就会堵塞
         */
        public void startServer() {

            serverSocket = NetPack.serverBind(port);
            StdOut.println("Server on port " + port
                    + " started! Waiting for client ...");
            while (!serverSocket.isClosed()) {
                if ((socket = NetPack.serverAccept(serverSocket)) == null) {
                    break;
                }
                socketList.add(socket);
                thread = new Thread(this);
                threadList.add(thread);
                thread.start();
            }
        }

        @Override
        public void run() {

            StdOut.println("Server on port " + port + ": Host: "
                    + socket.getInetAddress().getHostAddress() + " Connected!");
            callingClass.accept(socketList.size() - 1);
        }
    }

    /**
     * 获得socket列表
     * 
     * @return socket列表
     */
    public ArrayList<Socket> getSocketList() {

        return socketList;
    }

    /**
     * 以端口号构造
     * 
     * @param port
     *            端口号
     */
    public Server(Acceptor callingClass, int port) {
        setName(callingClass.getClass().getName() + " : Server");
        this.callingClass = callingClass;
        this.port = port;
        socketList = new ArrayList<Socket>();
        threadList = new ArrayList<Thread>();

        innerServer = new InnerServer();
    }

    public void configServer(int port) {
        this.port = port;

    }

    /**
     * 开始监听,调用这个方法不会堵塞当前进程。这个方法等同于调用start(),
     */
    public void listen() {
        start();
    }

    @Override
    public void run() {
        innerServer.setName(this.getName() + " : innerServer");
        innerServer.startServer();
    }

    public void close() {
        if (!serverSocket.isClosed()) {
            NetPack.serverClose(serverSocket);
        }
    }
}
